﻿/*
 *  Copyright 2012 HEINER DIRECT GmbH & Co. KG
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * 
 * @Author Rafael Dabrowski
 * 
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Client;
using System.Threading;
using System.Windows.Threading;
using SP = Microsoft.SharePoint.Client;
using System.ComponentModel;

namespace HDISpTagImporter
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public ClientContext context;
        public Web site;
        public ListCollection lists;


        public int imports = 0;
        public int errors = 0;
        public int notFound = 0;
        public int neue = 0;

        public bool isRunning = false;

        public MainWindow()
        {
            InitializeComponent();
        }

        public void getLists()
        {
            try
            {
                context = new ClientContext(tbSite.Text);

                site = context.Web;
                context.Load(site, osite => osite.Title);
                context.ExecuteQuery();

                Title = "HDI Tag Importer: " + site.Title;

                lists = site.Lists;

                IEnumerable<SP.List> listsCollection =
                    context.LoadQuery(lists.Include(l => l.Title, l => l.Id));
                context.ExecuteQuery();

                lbLists.ItemsSource = listsCollection;
                lbLists.DisplayMemberPath = "Title";
                doLog(lbLists.Items.Count.ToString() + " Listen in " + tbSite.Text + " gefunden.");
            }
            catch (Exception ex)
            {

                doLog(ex.Message, true);
            }
        }

        private void bGetLists_Click(object sender, RoutedEventArgs e)
        {
            getLists();
        }


        public void doLog(string text, bool fehler = false)
        {

            SimpleDelegate del1 = delegate()
            {
                ListBoxItem item = new ListBoxItem();
                item.Content = text;
                item.ToolTip = text;
                if (fehler)
                {
                    item.Background = Brushes.Red;
                    item.Foreground = Brushes.White;
                }
                int i = tbLog.Items.Add(item);
                tbLog.ScrollIntoView(tbLog.Items[i]);
                return 0;
            };
            this.Dispatcher.BeginInvoke(DispatcherPriority.Send, del1);
        }

        private void bImport_Click(object sender, RoutedEventArgs e)
        {
            if (!isRunning)
            {
                this.Cursor = Cursors.Wait;
                lbLists.IsEnabled = false;
                bGetLists.IsEnabled = false;
                bImport.Content = "Stop";
                isRunning = true;

                worker = new BackgroundWorker();

                worker.DoWork += new DoWorkEventHandler(RunOnBGThread);

                worker.RunWorkerCompleted += new
                  RunWorkerCompletedEventHandler(doUpdateDone);

                worker.RunWorkerAsync();
            }
            else
            {
                isRunning = false;
            }
        }
        private BackgroundWorker worker;
        private delegate object SimpleDelegate();

        private void RunOnBGThread(object sender, DoWorkEventArgs e)
        {
            doUpdate();
        }


        private void setBar(int i, int j)
        {

            SimpleDelegate del1 = delegate()
            {
                pbBar.Value = i;
                tbBar.Text = i + " von " + j + " ausgeführt.";
                return 0;
            };
            this.Dispatcher.BeginInvoke(DispatcherPriority.Send, del1);
        }



        private void doUpdate()
        {
            imports = 0;
            errors = 0;
            notFound = 0;
            neue = 0;
            int zaehl = 0;
            String contentType = "";
            //String[] zeilen = tbCSV.Text.Split('\n');

            SimpleDelegate del1 = delegate()
            {
                return new List<string>(tbCSV.Text.Split('\n'));
            };
            List<string> zeilen = (List<string>)this.Dispatcher.Invoke(DispatcherPriority.Send, del1);
            SimpleDelegate getCT = delegate()
            {
                return tbContentType.Text;
            };
            contentType = this.Dispatcher.Invoke(DispatcherPriority.Send, getCT) as String;
            SimpleDelegate del3 = delegate()
            {
                return cbNew.IsChecked.Value;
            };

            bool insert = (bool)Dispatcher.Invoke(DispatcherPriority.Send, del3);
            List<string> _Felder = new List<String>();
            List<String> Felder = new List<string>();
            _Felder.AddRange(zeilen[0].Split(';'));
            zeilen.RemoveAt(0);
            SimpleDelegate del2 = delegate()
            {
                pbBar.Maximum = zeilen.Count;
                return zeilen.Count;
            };

            this.Dispatcher.Invoke(DispatcherPriority.Send, del2);

            try
            {
                doLog("-----------------------------------");
                doLog("Import Gestartet");
                SP.List list = null;
                SimpleDelegate del6 = delegate()
                {
                    list = (SP.List)lbLists.SelectedValue;
                    return list;
                };
                list = (SP.List)this.Dispatcher.Invoke(DispatcherPriority.Send, del6);


                SP.ListItem item = null;
                context.Load(list);

                SP.ContentType ct = null;

                FieldCollection listfields = null;
                if (String.IsNullOrEmpty(contentType))
                {
                    listfields = list.Fields;
                }
                else
                {
                    SP.ContentTypeCollection lct = list.ContentTypes;
                    context.Load(lct, ts => ts.Include(t => t.Id, t => t.Name, tbBar => tbBar.Parent, t => t.Fields));
                    var res = context.LoadQuery(lct.Where(c => c.Name == contentType));
                    context.ExecuteQuery();
                    ct = res.FirstOrDefault<SP.ContentType>();
                    listfields = ct.Fields;
                }
                context.Load(listfields);
                context.ExecuteQuery();
                List<FieldType> ft = new List<FieldType>();
                int fi = 0;
                foreach (string f in _Felder)
                {
                    try
                    {
                        string field = f.Trim();
                        SP.Field oFeld = listfields.GetByInternalNameOrTitle(field);
                        context.Load(oFeld);
                        context.ExecuteQuery();
                        ft.Add(oFeld.FieldTypeKind);
                        String lookup = "";
                        if (oFeld.FieldTypeKind == FieldType.Lookup)
                        {
                            FieldLookup fl = context.CastTo<FieldLookup>(oFeld);
                            context.Load(fl);
                            context.ExecuteQuery();
                            lookup = ":" + fl.LookupList + ":" + fl.LookupField;
                        }
                        Felder.Add(oFeld.InternalName + lookup);
                    }
                    catch
                    {
                        doLog("Probleme den Feldtyp von: '" + f + "' zu bestimmen. Feld vorhanden?", true);
                        ft.Add(FieldType.Text);
                        Felder.Add(f);
                    }
                    fi++;
                }
                foreach (String i in zeilen)
                {
                    String[] zeile = i.Split(';');
                    try
                    {
                        item = getItem(list, Felder[0] + ":" + zeile[0]);
                        zaehl++;
                        setBar(zaehl, zeilen.Count);
                        int startfield = 1;

                        if (item == null)
                        {
                            //No item found
                            if (insert)
                            {
                                //Insert is Marked
                                if (list.BaseType.ToString() == "DocumentLibrary")
                                {
                                    //is aDocument Library
                                    FileCreationInformation newFile = new FileCreationInformation();
                                    context.Load(list);
                                    context.Load(list.RootFolder);
                                    context.ExecuteQuery();
                                    string path = zeile[((zeile.Count()) - 1)].Trim();
                                    newFile.Content = System.IO.File.ReadAllBytes(path);
                                    newFile.Url = list.RootFolder.ServerRelativeUrl + "/" + System.IO.Path.GetFileName(path);


                                    SP.File uploadFile = list.RootFolder.Files.Add(newFile);
                                    if (ct != null)
                                    {
                                        uploadFile.ListItemAllFields["ContentTypeId"] = ct.Id.ToString();
                                        uploadFile.ListItemAllFields.Update();
                                    }
                                    context.Load(uploadFile);
                                    context.ExecuteQuery();
                                    item = uploadFile.ListItemAllFields;
                                    //Set the metadata
                                    item.Update();
                                    context.ExecuteQuery();
                                    startfield = 0;
                                }
                                else
                                {
                                    //Is a List
                                    ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
                                    item = list.AddItem(itemCreateInfo);
                                    item[Felder[0]] = zeile[0];
                                    if (ct != null) item["ContentTypeId"] = ct.Id.ToString();
                                    item.Update();
                                    context.ExecuteQuery();
                                }
                                context.ExecuteQuery();
                                doLog("Hinzugefügt: " + zeile[0]);
                                neue++;
                            }
                            else
                            {
                                doLog("Eintrag: '" + zeile[0] + "' nicht Gefunden", true);
                                notFound++;
                                continue;
                            }
                        }//if item == 0

                        for (int j = startfield; j < Felder.Count; j++)
                        {
                            if (!String.IsNullOrEmpty(zeile[j]))
                            {
                                string wert = zeile[j].Trim();
                                string feld = Felder[j].Trim();
                                if (!String.IsNullOrEmpty(wert))
                                {
                                    switch (ft[j])
                                    {
                                        case FieldType.DateTime:
                                            try
                                            {
                                                item[feld] = DateTime.Parse(wert);
                                            }
                                            catch
                                            {
                                                errors++;
                                                doLog(zaehl + ": Konnte Zeitformat nicht Umwandeln \n" + feld + ": " + wert, true);
                                            }
                                            break;
                                        case FieldType.User:
                                            try
                                            {
                                                try
                                                {
                                                    item.Update();
                                                    context.ExecuteQuery();
                                                }
                                                catch (Exception ex) { errors++; doLog(zaehl + ": " + ex.Message, true); }
                                                FieldUserValue usr = FieldUserValue.FromUser(wert);
                                                item[feld] = usr;
                                                item.Update();
                                                context.ExecuteQuery();
                                            }
                                            catch
                                            {
                                                errors++;
                                                doLog(zaehl + ":Konnte Benutzer '" + wert + "' nicht finden.", true);
                                            }
                                            break;
                                        case FieldType.Number:
                                            try
                                            {
                                                item[feld] = Double.Parse(wert);
                                            }
                                            catch
                                            {
                                                errors++;
                                                doLog(zaehl + ": " + wert + " is keine Zahl", true);
                                            }
                                            break;
                                        case FieldType.Boolean:
                                            item[feld] = wert;
                                            break;
                                        case FieldType.MultiChoice:
                                            List<String> sVal = new List<String>();
                                            foreach (String s in wert.Split('|'))
                                            {
                                                sVal.Add(s);
                                            }
                                            item[feld] = sVal;
                                            break;
                                        case FieldType.Lookup:
                                            try
                                            {
                                                List<SP.FieldLookupValue> flval = new List<SP.FieldLookupValue>();
                                                foreach (String s in wert.Split('|'))
                                                {
                                                    SP.FieldLookupValue val = new SP.FieldLookupValue();
                                                    SP.List source = lists.GetById(Guid.Parse(feld.Split(':')[1]));
                                                    SP.Field oField = source.Fields.GetByInternalNameOrTitle(feld.Split(':')[2].Trim());
                                                    context.Load(oField);
                                                    context.ExecuteQuery();
                                                    int id = getItem(source, oField.InternalName + ":" + s).Id;
                                                    val.LookupId = id;
                                                    flval.Add(val);
                                                }
                                                item[feld.Split(':')[0]] = flval;
                                            }
                                            catch
                                            {
                                                errors++;
                                                doLog(zaehl + ": Lookup von '" + wert + "' war nicht erfolgreich", true);
                                            }
                                            break;
                                        case FieldType.Attachments:

                                            FileStream fileStream = new FileStream(wert, FileMode.Open);
                                            string attachmentPath = string.Format("/Lists/{1}/Attachments/{2}/{3}", site.Title, list.Title, item.Id, System.IO.Path.GetFileName(wert));
                                            doLog(attachmentPath, true);
                                            SP.File.SaveBinaryDirect(context, attachmentPath, fileStream, true);


                                            break;
                                        default:
                                            item[feld] = zeile[j];
                                            break;
                                    }
                                }
                            }
                            item.Update();
                        }
                        item.Update();
                        context.ExecuteQuery();
                        imports++;

                    }
                    catch (Exception ex)
                    {
                        doLog(zaehl + ": Fehler bei: " + zeile[0] + "\nFehler: " + ex.Message, true);
                        errors++;
                    }



                    if (!isRunning)
                    {
                        throw new Exception("Vorgang abgebrochen bei: " + zaehl + " " + zeile[0]);
                    }
                }
            }
            catch (Exception ex)
            {
                doLog(ex.Message, true);
                errors++;
            }
        }


        private void doUpdateDone(object sender, RunWorkerCompletedEventArgs e)
        {
            doLog("Vorgang abgeschlossen: \nSoll: " + (tbCSV.Text.Split('\n').Count() - 1) +
          "\nUpdates: " + imports +
          "\nNeue: " + neue +
          "\nNicht Gefunden: " + notFound +
          "\nFehlerhaft: " + errors);
            this.Cursor = Cursors.Arrow;
            lbLists.IsEnabled = true;
            bGetLists.IsEnabled = true;
            bImport.Content = "los";
            isRunning = false;
        }


        public SP.ListItem getItem(SP.List _list, String value)
        {
            String[] Felder = value.Split(':');
            SP.CamlQuery x = new SP.CamlQuery();
            x.ViewXml =
             @"<View>
                          <Query>
                                <Where>
                                    <Eq>
                                        <FieldRef Name='" + Felder[0] + @"'/>
                                        <Value Type='Text'>" + Felder[1] + @"</Value>
                                    </Eq>
                                </Where>
                            </Query>
                            <RowLimit>1</RowLimit>
                        </View>";



            SP.ListItemCollection liste = _list.GetItems(x);
            context.Load(liste);
            context.ExecuteQuery();
            if (liste.Count > 0)
            {
                return liste[0];
            }
            return null;
        }

        private void label7_MouseDown(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show(
@"Anleitung: 
Serveradresse eintragen > Listen Laden > Liste Auswählen > CSV Daten reinkopieren > importieren.

In die erste Zeile müssen die Felder die befüllt werden sollen.

Wichtig: 
Nachschlagefelder brauchen zusätzliche Informationen. Zieltabelle und Zielspalte.
Die Form ist dann: Spalte:Zieltabelle:Zielspalte

Zum Hochladen von Dateien in Bibliotheken, den PFad in die letzte Spalte schreiben.

Standardfelder:
Titel: Title
Dokument Name:  FileLeafRef
Bearbeitet:     Modified
Bearbeitet von: Editor
Zugewiesen an:  AssignedTo
Erstellt:       Created
Erstellt von:   Author 
Vollständighe Liste: http://tinyurl.com/3gr2ewf

Sonderzeichen: 
ä = _x00e4
ü = _x00fc_
ö = _x00f6_
ß = _x00df_
[Leerzeichen] = _x0020_ 
", "Hilfe");

        }


        public string getListType()
        {
            SP.List list = (SP.List)lbLists.SelectedValue;
            context.Load(list);
            context.ExecuteQuery();
            return list.BaseType.ToString();
        }

        private void tbSite_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                getLists();
                lbLists.Focus();
            }
        }

        private void lbLists_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // SP.List list = (SP.List)lbLists.SelectedValue;
            // context.Load(list.RootFolder);
            // context.ExecuteQuery();
            // doLog(list.RootFolder.ServerRelativeUrl);
        }
    }
}
